{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![Django Ledger](../django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@2x.png \"Django Ledger Logo\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.057266Z",
     "start_time": "2024-10-17T22:43:00.621298Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import os\n",
    "from datetime import date, datetime\n",
    "from decimal import Decimal\n",
    "from typing import Optional, Union\n",
    "from zoneinfo import ZoneInfo\n",
    "\n",
    "import django\n",
    "# for easier visualization it is recommended to use pandas to render data...\n",
    "# if pandas is not installed, you may install it with this command: pip install -U pandas\n",
    "# pandas is not a dependency of django_ledger...\n",
    "from django.core.exceptions import ObjectDoesNotExist\n",
    "import pandas as pd\n",
    "\n",
    "# Set your django settings module if needed...\n",
    "os.environ['DJANGO_SETTINGS_MODULE'] = 'dev_env.settings'\n",
    "\n",
    "# if using jupyter notebook need to set DJANGO_ALLOW_ASYNC_UNSAFE as \"true\"\n",
    "os.environ['DJANGO_ALLOW_ASYNC_UNSAFE'] = 'true'\n",
    "\n",
    "# change your working directory as needed...\n",
    "os.chdir('../')\n",
    "\n",
    "django.setup()\n",
    "\n",
    "from django.contrib.auth import get_user_model\n",
    "from django_ledger.io import roles"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get Your Entity Administrator UserModel"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.064978Z",
     "start_time": "2024-10-17T22:43:01.059758Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# change this to your preferred django username...\n",
    "MY_USERNAME = 'ceo_user'\n",
    "MY_PASSWORD = 'NeverUseMe|VeryInsecure!'\n",
    "UserModel = get_user_model()\n",
    "\n",
    "try:\n",
    "    user_model = UserModel.objects.get(username__exact=MY_USERNAME)\n",
    "except ObjectDoesNotExist:\n",
    "    user_model = UserModel(username=MY_USERNAME)\n",
    "    user_model.set_password(MY_PASSWORD)\n",
    "    user_model.save()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Get or Create an Entity Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.124908Z",
     "start_time": "2024-10-17T22:43:01.116147Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from django_ledger.models.entity import EntityModel\n",
    "\n",
    "ENTITY_NAME = 'One Big Company, LLC'\n",
    "\n",
    "entity_model = EntityModel.create_entity(\n",
    "    name=ENTITY_NAME,\n",
    "    admin=user_model,\n",
    "    use_accrual_method=True,\n",
    "    fy_start_month=1\n",
    ")\n",
    "\n",
    "entity_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chart of Accounts (CoA)\n",
    "- A Chart of Accounts is a user-defined list of accounts. \n",
    "- Each Entity Model must have at least one default Chart of Accounts."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a Default Chart of Accounts\n",
    "- Newly created EntityModel do not have a default Code of Accounts yet.\n",
    "- Django Ledger support multiple chart of accounts, but only one can be assigned as default."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Check if entity has a default CoA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.130228Z",
     "start_time": "2024-10-17T22:43:01.128396Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "entity_model.has_default_coa()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.168174Z",
     "start_time": "2024-10-17T22:43:01.133951Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "default_coa_model = entity_model.create_chart_of_accounts(\n",
    "    assign_as_default=True,\n",
    "    commit=True,\n",
    "    coa_name='My QuickStart CoA'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.173469Z",
     "start_time": "2024-10-17T22:43:01.171668Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "default_coa_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### EntityModel has now a Default Chart of Accounts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.178754Z",
     "start_time": "2024-10-17T22:43:01.176835Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "entity_model.has_default_coa()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.183755Z",
     "start_time": "2024-10-17T22:43:01.181910Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "default_coa_model = entity_model.get_default_coa()\n",
    "default_coa_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Default Chart of Accounts is accessible from the Entity Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.189Z",
     "start_time": "2024-10-17T22:43:01.187138Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "entity_model.default_coa == default_coa_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Django Ledger support multiple chart of accounts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.225886Z",
     "start_time": "2024-10-17T22:43:01.192541Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "another_coa_model = entity_model.create_chart_of_accounts(\n",
    "    assign_as_default=False,\n",
    "    commit=True,\n",
    "    coa_name='My Empty Chart of Accounts'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.231327Z",
     "start_time": "2024-10-17T22:43:01.229445Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "another_coa_model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Populate Entity with Random Data (Optional)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you are getting started with Django Ledger, you may want to populate an entity with random data to help you get familiar with the API."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define a Start Date for Transactions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:01.236468Z",
     "start_time": "2024-10-17T22:43:01.234715Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "START_DTTM = datetime(year=2022, month=10, day=1, tzinfo=ZoneInfo('UTC'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "### Fill the entity with random data.\n",
    "- This action will populate the EntityModel with random data.\n",
    "- It will populate a Code of Accounts using a default pre-defined list.\n",
    "- This approach is for illustration, educational and testing purposes, not encouraged for new production entities.\n",
    "- Only Entities with no transactions can use this method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.443235Z",
     "start_time": "2024-10-17T22:43:01.239552Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "entity_model.populate_random_data(start_date=START_DTTM)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Accounts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "## Default CoA Accounts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.454296Z",
     "start_time": "2024-10-17T22:43:04.447217Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "default_coa_accounts_qs = entity_model.get_default_coa_accounts()\n",
    "pd.DataFrame(default_coa_accounts_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get CoA Accounts by CoA Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.466475Z",
     "start_time": "2024-10-17T22:43:04.461949Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "coa_accounts_by_coa_model_qs = entity_model.get_coa_accounts(coa_model=default_coa_model)\n",
    "pd.DataFrame(coa_accounts_by_coa_model_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "No Accounts yet on this CoA..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.471463Z",
     "start_time": "2024-10-17T22:43:04.468341Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "coa_accounts_by_coa_model_qs = entity_model.get_coa_accounts(coa_model=another_coa_model)\n",
    "pd.DataFrame(coa_accounts_by_coa_model_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get CoA Accounts by CoA Model UUID\n",
    "- May pass UUID instance instead of ChartOF AccountsModel..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.478052Z",
     "start_time": "2024-10-17T22:43:04.473189Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "coa_accounts_by_coa_uuid_qs = entity_model.get_coa_accounts(coa_model=default_coa_model.uuid)\n",
    "pd.DataFrame(coa_accounts_by_coa_uuid_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get CoA Accounts by CoA Model Slug\n",
    "- If string is passed, will lookup by slug..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.484276Z",
     "start_time": "2024-10-17T22:43:04.479817Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "coa_accounts_by_coa_slug_qs = entity_model.get_coa_accounts(coa_model=default_coa_model.slug)\n",
    "pd.DataFrame(coa_accounts_by_coa_slug_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get Accounts With Codes and CoA Model\n",
    "- Assumes default CoA if no coa_model is passed..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.489782Z",
     "start_time": "2024-10-17T22:43:04.486037Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "coa_accounts_by_codes_qs = entity_model.get_accounts_with_codes(code_list=['1010', '1050'])\n",
    "pd.DataFrame(coa_accounts_by_codes_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Empty ChartOfAccountModel..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.494621Z",
     "start_time": "2024-10-17T22:43:04.491620Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "coa_accounts_by_codes_qs = entity_model.get_accounts_with_codes(\n",
    "    code_list=['1010', '1050'],\n",
    "    coa_model=another_coa_model\n",
    ")\n",
    "pd.DataFrame(coa_accounts_by_codes_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "### Get All Accounts at Once"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.500968Z",
     "start_time": "2024-10-17T22:43:04.496331Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "coa_qs, coa_map = entity_model.get_all_coa_accounts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "A dictionary, CoA Model -> Account List."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.508596Z",
     "start_time": "2024-10-17T22:43:04.505474Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "coa_map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.514962Z",
     "start_time": "2024-10-17T22:43:04.510373Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "pd.DataFrame(coa_map[default_coa_model])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.519443Z",
     "start_time": "2024-10-17T22:43:04.516499Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "pd.DataFrame(coa_map[another_coa_model])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create Account Model\n",
    "- Creating AccountModel into empty \"another_coa_model\"..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.525885Z",
     "start_time": "2024-10-17T22:43:04.521084Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "account_model = entity_model.create_account(\n",
    "    coa_model=another_coa_model,\n",
    "    code='1220',\n",
    "    role=roles.ASSET_CA_INVENTORY,\n",
    "    name='A new account created from the EntityModel API!',\n",
    "    balance_type=roles.DEBIT,\n",
    "    active=True\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.529051Z",
     "start_time": "2024-10-17T22:43:04.527339Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "account_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.534252Z",
     "start_time": "2024-10-17T22:43:04.530602Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "another_coa_accounts_qs = entity_model.get_coa_accounts(coa_model=another_coa_model)\n",
    "pd.DataFrame(another_coa_accounts_qs.values('code', 'name', 'role', 'balance_type', 'active', 'locked'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "# Basic Django Ledger Usage\n",
    "- The LedgerModel name is whatever your heart desires.\n",
    "- Examples:\n",
    "    - A month.\n",
    "    - A customer.\n",
    "    - A vendor.\n",
    "    - A project.\n",
    "    - A building.\n",
    "- The more ledgers are created, the more segregation and control over transactions is possible."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.537988Z",
     "start_time": "2024-10-17T22:43:04.535905Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "ledger_model = entity_model.create_ledger(\n",
    "    name='My October 2023 Ledger',\n",
    "    posted=True\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create a Library"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.542060Z",
     "start_time": "2024-10-17T22:43:04.539509Z"
    }
   },
   "outputs": [],
   "source": [
    "from django_ledger.io.io_library import IOLibrary\n",
    "\n",
    "library = IOLibrary(name='djangocon-2024-library')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Create and Register a BluePrint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.545423Z",
     "start_time": "2024-10-17T22:43:04.543560Z"
    }
   },
   "outputs": [],
   "source": [
    "from django_ledger.io.io_library import IOBluePrint\n",
    "\n",
    "\n",
    "@library.register\n",
    "def sale_blueprint(\n",
    "        sale_amount: Union[int, float, Decimal],\n",
    "        contribution_margin_percent: float,\n",
    "        description: Optional[str] = None\n",
    ") -> IOBluePrint:\n",
    "    blueprint = IOBluePrint()\n",
    "    cogs_amount = (1 - contribution_margin_percent) * sale_amount\n",
    "    blueprint.debit(account_code='1010', amount=sale_amount, description=description)\n",
    "    blueprint.credit(account_code='4010', amount=sale_amount, description=description)\n",
    "    blueprint.credit(account_code='1200', amount=cogs_amount, description=description)\n",
    "    blueprint.debit(account_code='5010', amount=cogs_amount, description=description)\n",
    "    return blueprint"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get a Cursor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.548402Z",
     "start_time": "2024-10-17T22:43:04.546969Z"
    }
   },
   "outputs": [],
   "source": [
    "cursor = library.get_cursor(\n",
    "    entity_model=entity_model,\n",
    "    user_model=user_model\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dispatch Instructions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.551432Z",
     "start_time": "2024-10-17T22:43:04.549906Z"
    }
   },
   "outputs": [],
   "source": [
    "# Option 1 - Use A Ledger Model\n",
    "cursor.dispatch('sale_blueprint',\n",
    "                ledger_model=ledger_model,\n",
    "                sale_amount=34.45,\n",
    "                contribution_margin_percent=0.13,\n",
    "                description='Order ID: 123')\n",
    "\n",
    "# Option 2- Create a New Ledger Model\n",
    "cursor.dispatch('sale_blueprint',\n",
    "                ledger_model='ledger-order-id-123',\n",
    "                sale_amount=90.43,\n",
    "                contribution_margin_percent=0.17,\n",
    "                description='Order ID: 123')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Commit Your Instructions\n",
    "Not recommended to post both ledger and journal entries. Posted transactions will immediately hit the books.\n",
    "**result** contains resulting ledger models, journal entries and transactions fro the committed "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.569400Z",
     "start_time": "2024-10-17T22:43:04.552930Z"
    }
   },
   "outputs": [],
   "source": [
    "stub = cursor.commit(\n",
    "    post_new_ledgers=True,\n",
    "    post_journal_entries=True,\n",
    "    je_timestamp=datetime(2023, 12, 2, 12, 10)\n",
    "    # je_timestamp='2023-12-02 12:10'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "### Get Financial Statement Report Data for Ledger Model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Balance Sheet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.574264Z",
     "start_time": "2024-10-17T22:43:04.571195Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "bs_data = ledger_model.digest_balance_sheet(\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "\n",
    "bs_data.get_balance_sheet_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Income Statement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.579583Z",
     "start_time": "2024-10-17T22:43:04.575796Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "is_data = ledger_model.digest_income_statement(\n",
    "    from_date=date(2023, 1, 1),\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "\n",
    "is_data.get_income_statement_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "Cash Flow Statement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.584313Z",
     "start_time": "2024-10-17T22:43:04.581171Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "cfs_data = ledger_model.digest_cash_flow_statement(\n",
    "    from_date=date(2023, 1, 1),\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "\n",
    "cfs_data.get_cash_flow_statement_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "All Statements in a Single Call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.588446Z",
     "start_time": "2024-10-17T22:43:04.585815Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "fin_digest = ledger_model.digest_financial_statements(\n",
    "    from_date=date(2023, 1, 1),\n",
    "    to_date=date(2023, 12, 31),\n",
    "    entity_slug=entity_model\n",
    ")\n",
    "\n",
    "statement_data = fin_digest.get_financial_statements_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.592031Z",
     "start_time": "2024-10-17T22:43:04.590096Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "statement_data['balance_sheet']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.595650Z",
     "start_time": "2024-10-17T22:43:04.593602Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "statement_data['income_statement']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.599058Z",
     "start_time": "2024-10-17T22:43:04.597132Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "statement_data['cash_flow_statement']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Financial Statement PDF Reports"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Set Up\n",
    "- Must enable PDF support by installing dependencies via *pipenv*.\n",
    "    - pipenv install --categories pdf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Balance Sheet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.632776Z",
     "start_time": "2024-10-17T22:43:04.600592Z"
    }
   },
   "outputs": [],
   "source": [
    "bs_report = entity_model.get_balance_sheet_statement(\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")\n",
    "bs_data = bs_report.get_report_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.650578Z",
     "start_time": "2024-10-17T22:43:04.648119Z"
    }
   },
   "outputs": [],
   "source": [
    "bs_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Balance Sheet Statement Raw Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.675190Z",
     "start_time": "2024-10-17T22:43:04.672615Z"
    }
   },
   "outputs": [],
   "source": [
    "bs_report.get_report_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Income Statement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.685609Z",
     "start_time": "2024-10-17T22:43:04.677258Z"
    }
   },
   "outputs": [],
   "source": [
    "ic_report = entity_model.get_income_statement(\n",
    "    from_date=date(2022, 1, 1),\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")\n",
    "\n",
    "ic_data = ic_report.get_report_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Income Statement Raw Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.691474Z",
     "start_time": "2024-10-17T22:43:04.688862Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "ic_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cash Flow Statement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.712598Z",
     "start_time": "2024-10-17T22:43:04.704610Z"
    }
   },
   "outputs": [],
   "source": [
    "cf_report = entity_model.get_cash_flow_statement(\n",
    "    from_date=date(2022, 1, 1),\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")\n",
    "\n",
    "cf_data = cf_report.get_report_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cash Flow Statement Raw Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.717768Z",
     "start_time": "2024-10-17T22:43:04.715672Z"
    }
   },
   "outputs": [],
   "source": [
    "cf_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## All Financial Statements Data in a single Call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.741130Z",
     "start_time": "2024-10-17T22:43:04.723689Z"
    }
   },
   "outputs": [],
   "source": [
    "reports = entity_model.get_financial_statements(\n",
    "    user_model=user_model,\n",
    "    from_date=date(2022, 1, 1),\n",
    "    to_date=date(2022, 12, 31),\n",
    "    save_pdf=True,\n",
    "    filepath='./'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.745570Z",
     "start_time": "2024-10-17T22:43:04.744149Z"
    }
   },
   "outputs": [],
   "source": [
    "bs_data = reports.balance_sheet_statement.get_report_data()\n",
    "ic_data = reports.income_statement.get_report_data()\n",
    "cf_data = reports.cash_flow_statement.get_report_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.756599Z",
     "start_time": "2024-10-17T22:43:04.753950Z"
    }
   },
   "outputs": [],
   "source": [
    "reports.income_statement.get_report_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.761514Z",
     "start_time": "2024-10-17T22:43:04.759039Z"
    }
   },
   "outputs": [],
   "source": [
    "reports.cash_flow_statement.get_report_data()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Extending Django Ledger"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simple Payroll Example\n",
    "**NOTE**: This will not work in the context of a jupyter notebook"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:04.895508Z",
     "start_time": "2024-10-17T22:43:04.764392Z"
    }
   },
   "outputs": [],
   "source": [
    "from django.db import models\n",
    "from django_ledger.models import LedgerModel\n",
    "from django_ledger.io.io_library import IOLibrary\n",
    "\n",
    "# Create A Payroll Library\n",
    "payroll_library = IOLibrary('payroll')\n",
    "\n",
    "# Register a Blueprint...\n",
    "@payroll_library.register\n",
    "def process_employee_payroll(gross_pay, tax_bracket):\n",
    "    deductions = gross_pay * tax_bracket\n",
    "    payout = gross_pay - deductions\n",
    "    bp = IOBluePrint()\n",
    "    bp.credit(account_code='1010', amount=gross_pay)  # Bank Account\n",
    "    bp.debit(account_code='6070', amount=payout)  # Wages Expense\n",
    "    bp.debit(account_code='6210', amount=deductions)  # Payroll Taxes\n",
    "    return bp\n",
    "\n",
    "\n",
    "# Extend the Ledger Model...\n",
    "class EmployeeModel(LedgerModel):\n",
    "    first_name = models.CharField(max_length=50)\n",
    "    last_name = models.CharField(max_length=50)\n",
    "    dob = models.DateField()\n",
    "    salary = models.DecimalField()\n",
    "    tax_bracket = models.DecimalField()\n",
    "\n",
    "    def get_gross_pay(self):\n",
    "        # bi-monthly payments...\n",
    "        return self.salary / 24\n",
    "\n",
    "    def process_payroll(self, pay_date, user_model):\n",
    "        cursor = payroll_library.get_cursor(\n",
    "            entity_model=self.entity,\n",
    "            user_model=user_model\n",
    "        )\n",
    "        gross_pay = self.get_gross_pay()\n",
    "        cursor.dispatch('process_employee_payroll',\n",
    "                        gross_pay=gross_pay,\n",
    "                        tax_bracket=self.tax_bracket)\n",
    "        return cursor.commit(\n",
    "            post_new_ledgers=False,\n",
    "            post_journal_entries=True,\n",
    "            je_timestamp=pay_date\n",
    "        )\n",
    "\n",
    "    def send_employee_payroll_report(self, from_date, to_date, user_model):\n",
    "        financial_data = self.get_income_statement(\n",
    "            from_date=from_date,\n",
    "            to_date=to_date,\n",
    "            user_model=user_model)\n",
    "        # Send report...\n",
    "        return financial_data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Billing / Invoicing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:26.296503Z",
     "start_time": "2024-10-17T22:43:26.293678Z"
    }
   },
   "outputs": [],
   "source": [
    "from django_ledger.models import BillModel, ItemTransactionModel\n",
    "from random import choices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:26.877384Z",
     "start_time": "2024-10-17T22:43:26.853213Z"
    }
   },
   "outputs": [],
   "source": [
    "vendor_model = entity_model.get_vendors().first()\n",
    "bill_model = entity_model.create_bill(\n",
    "    vendor_model=vendor_model,\n",
    "    terms=BillModel.TERMS_NET_30\n",
    ")\n",
    "bill_model\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:27.689789Z",
     "start_time": "2024-10-17T22:43:27.673651Z"
    }
   },
   "outputs": [],
   "source": [
    "expense_items_qs = choices(entity_model.get_items_for_bill(), k=3)\n",
    "bill_items = [\n",
    "    ItemTransactionModel(\n",
    "        item_model=i,\n",
    "        quantity=3,\n",
    "        unit_cost=5,\n",
    "        bill_model=bill_model\n",
    "    ) for i in expense_items_qs\n",
    "]\n",
    "for i in bill_items:\n",
    "    i.clean()\n",
    "bill_model.itemtransactionmodel_set.bulk_create(bill_items)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:28.533095Z",
     "start_time": "2024-10-17T22:43:28.528389Z"
    }
   },
   "outputs": [],
   "source": [
    "item_model_qs = bill_model.update_amount_due()\n",
    "bill_model.amount_due, bill_model.amount_paid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:29.119599Z",
     "start_time": "2024-10-17T22:43:29.079770Z"
    }
   },
   "outputs": [],
   "source": [
    "bill_model.mark_as_review(commit=True)\n",
    "bill_model.mark_as_approved(commit=True, user_model=user_model)\n",
    "bill_model.mark_as_paid(commit=True, user_model=user_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-10-17T22:43:29.727243Z",
     "start_time": "2024-10-17T22:43:29.723964Z"
    }
   },
   "outputs": [],
   "source": [
    "bill_model.amount_due, bill_model.amount_paid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
